package com.culturaltourism.core.spring;

import com.culturaltourism.core.exceptions.UnexpectedException;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * @ClassName: SysApplicationContext
 * @Description: 以静态方法的方式手动获取spring容器中的bean
 *               本类实现ApplicationContextAware接口，并重写setApplicationContext方法获取ApplicationContext实例，
 *              且修改ApplicationContext实例对象为静态实例，方法改为静态方法，
 *              这样在非Spring管理的实体内同样可以获取到指定Bean的实例。
 *              注意WxmsApplicationContext类上的@Component注解是不可以去掉的，
 *              去掉后Spring就不会自动调用setApplicationContext方法来为我们设置上下文实例。
 * @Author MaShuai
 * @Date 2019/5/2 17:54
 * @Version V1.0
 */
@Component
@Lazy(false)
public class SysApplicationContext implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    /**
     * 实现ApplicationContextAware接口的context注入函数, 将其存入静态变量.
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SysApplicationContext.applicationContext = applicationContext;
    }

    /**
     * 取得存储在静态变量中的ApplicationContext.
     */
    public ApplicationContext getApplicationContext(){
        return applicationContext;
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(String name) {
        checkApplicationContext();
        return (T) applicationContext.getBean(name);
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * 如果有多个Bean符合Class, 取出第一个.
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBean(Class<T> requiredType) {
        checkApplicationContext();
        Map<String,T> beans= applicationContext.getBeansOfType(requiredType);
        if(!beans.isEmpty()){
            return beans.values().iterator().next();
        }
        throw new UnexpectedException("spring中没有类型【"+requiredType.getName()+"】的实例");
    }

    /**
     * 从静态变量ApplicationContext中取得Bean, 自动转型为所赋值对象的类型.
     * 如果有多个Bean符合Class, 取出第一个.如果没有对应的类返回null
     */
    @SuppressWarnings("unchecked")
    public static <T> T getBeanByClassName(String clazz) {
        checkApplicationContext();
        try {
            Map<String,?> beans= applicationContext.getBeansOfType(Class.forName(clazz));
            if(!beans.isEmpty()){
                return (T)beans.values().iterator().next();
            }
            throw new UnexpectedException("spring中没有类型【"+clazz+"】的实例");
        } catch (Exception e) {
            throw new UnexpectedException(e.getMessage(),e);
        }
    }

    /**
     * 清除applicationContext静态变量.
     */
    public static void cleanApplicationContext(){
        applicationContext = null;
    }

    private static void checkApplicationContext() {
        if (applicationContext == null) {
            throw new IllegalStateException("applicaitonContext未注入,请在applicationContext.xml中定义SpringContextHolder");
        }
    }
}
